{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is a `bqplot` recreation of Mike Bostock's [Wealth of Nations](https://bost.ocks.org/mike/nations/). This was also done by [Gapminder](http://www.gapminder.org/world/#$majorMode=chart$is;shi=t;ly=2003;lb=f;il=t;fs=11;al=30;stl=t;st=t;nsl=t;se=t$wst;tts=C$ts;sp=5.59290322580644;ti=2013$zpv;v=0$inc_x;mmid=XCOORDS;iid=phAwcNAVuyj1jiMAkmq1iMg;by=ind$inc_y;mmid=YCOORDS;iid=phAwcNAVuyj2tPLxKvvnNPA;by=ind$inc_s;uniValue=8.21;iid=phAwcNAVuyj0XOoBL_n5tAQ;by=ind$inc_c;uniValue=255;gid=CATID0;by=grp$map_x;scale=log;dataMin=194;dataMax=96846$map_y;scale=lin;dataMin=23;dataMax=86$map_s;sma=49;smi=2.65$cd;bd=0$inds=;modified=60). It is originally based on a TED Talk by [Hans Rosling](http://www.ted.com/talks/hans_rosling_shows_the_best_stats_you_ve_ever_seen)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "import ipywidgets as widgets\n",
    "import bqplot as bq\n",
    "import bqplot.pyplot as plt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "initial_year = 1800"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": false
   },
   "outputs": [],
   "source": [
    "data = pd.read_json('nations.json')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def clean_data(data):\n",
    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
    "        data = data.drop(data[data[column].apply(len) <= 4].index)\n",
    "    return data\n",
    "\n",
    "def extrap_interp(data):\n",
    "    data = np.array(data)\n",
    "    x_range = np.arange(1800, 2009, 1.)\n",
    "    y_range = np.interp(x_range, data[:, 0], data[:, 1])\n",
    "    return y_range\n",
    "\n",
    "def extrap_data(data):\n",
    "    for column in ['income', 'lifeExpectancy', 'population']:\n",
    "        data[column] = data[column].apply(extrap_interp)\n",
    "    return data"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "data = clean_data(data)\n",
    "data = extrap_data(data)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "income_min, income_max = np.min(data['income'].apply(np.min)), np.max(data['income'].apply(np.max))\n",
    "life_exp_min, life_exp_max = np.min(data['lifeExpectancy'].apply(np.min)), np.max(data['lifeExpectancy'].apply(np.max))\n",
    "pop_min, pop_max = np.min(data['population'].apply(np.min)), np.max(data['population'].apply(np.max))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "def get_data(year):\n",
    "    year_index = year - 1800\n",
    "    income = data['income'].apply(lambda x: x[year_index])\n",
    "    life_exp = data['lifeExpectancy'].apply(lambda x: x[year_index])\n",
    "    pop =  data['population'].apply(lambda x: x[year_index])\n",
    "    return income, life_exp, pop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "time_interval = 100\n",
    "fig_layout = widgets.Layout(width='1000px', height='700px', overflow_x='hidden')\n",
    "fig = plt.figure(layout=fig_layout, fig_margin=dict(top=60, bottom=80, left=40, right=20),\n",
    "                 title='Health and Wealth of Nations', animation_duration=time_interval)\n",
    "\n",
    "plt.scales(scales={'x': bq.LogScale(min=min(200, income_min), max=income_max),\n",
    "                   'y': bq.LinearScale(min=life_exp_min, max=life_exp_max),\n",
    "                   'color': bq.OrdinalColorScale(domain=data['region'].unique().tolist(), \n",
    "                                                 colors=bq.CATEGORY10[:6]),\n",
    "                   'size': bq.LinearScale(min=pop_min, max=pop_max)})\n",
    "\n",
    "# add custom x tick values\n",
    "ticks = [2, 4, 6, 8, 10]\n",
    "income_ticks = [t * 100 for t in ticks] + [t * 1000 for t in ticks] + [t * 10000 for t in ticks]\n",
    "\n",
    "# custom axis options\n",
    "axes_options = {'x': dict(label='Income per Capita', label_location='end', \n",
    "                          label_offset='-2ex', tick_format='~s', \n",
    "                          tick_values=income_ticks),\n",
    "                'y': dict(label='Life Expectancy', orientation='vertical', \n",
    "                          side='left', label_location='end', label_offset='-1em'),\n",
    "                'color': dict(label='Region')\n",
    "               }\n",
    "\n",
    "tooltip = bq.Tooltip(fields=['name', 'x', 'y'], \n",
    "                     labels=['Country Name', 'Income per Capita', 'Life Expectancy'])\n",
    "\n",
    "year_label = bq.Label(x=[0.75], y=[0.10], default_size=46, font_weight='bolder', \n",
    "                      colors=['orange'], text=[str(initial_year)], enable_move=True)\n",
    "\n",
    "# Start with the first year's data\n",
    "cap_income, life_exp, pop = get_data(initial_year)\n",
    "\n",
    "wealth_scat = plt.scatter(cap_income, life_exp, color=data['region'], size=pop,\n",
    "                          names=data['name'], display_names=False,\n",
    "                          default_size=20000, tooltip=tooltip, stroke='Black',\n",
    "                          axes_options=axes_options, unhovered_style={'opacity': 0.5})\n",
    "nation_line = plt.plot(data['income'][0], data['lifeExpectancy'][0],\n",
    "                       colors=['Gray'], visible=False)\n",
    "\n",
    "# slider for the year\n",
    "year_slider = widgets.IntSlider(min=1800, max=2008, description='Year', value=initial_year)\n",
    "\n",
    "# register callbacks\n",
    "def hover_changed(change):\n",
    "    if change.new is not None:\n",
    "        nation_line.x = data[data['name'] == wealth_scat.names[change.new]]['income'].values[0]\n",
    "        nation_line.y = data[data['name'] == wealth_scat.names[change.new]]['lifeExpectancy'].values[0]\n",
    "        nation_line.visible = True\n",
    "    else:\n",
    "        nation_line.visible = False\n",
    "        \n",
    "wealth_scat.observe(hover_changed, 'hovered_point')\n",
    "\n",
    "def year_changed(change):\n",
    "    wealth_scat.x, wealth_scat.y, wealth_scat.size = get_data(year_slider.value)\n",
    "    year_label.text = [str(year_slider.value)]\n",
    "\n",
    "year_slider.observe(year_changed, 'value')\n",
    "\n",
    "play_button = widgets.Play(min=1800, max=2008, interval=time_interval)\n",
    "widgets.jslink((play_button, 'value'), (year_slider, 'value'))\n",
    "\n",
    "widgets.VBox([widgets.HBox([play_button, year_slider]), fig])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
